﻿using log4net;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;


namespace FileUpload.Controllers
{
    public class UploadController : Controller
    {
        private IWebHostEnvironment _hostingEnv;
        private readonly IConfiguration _config;

        private string fileUploadPath;
        private string[] fileTypes;
        private long fileMaxSize;


        public UploadController(IWebHostEnvironment hostingEnv, IConfiguration configuration)
        {
            _hostingEnv = hostingEnv;
            _config = configuration;

            //serverHost = _config["App:ServerHost"];         

            fileUploadPath = $"{_config["UploadConfig:File:Path"]}/{DateTime.Now:yyyyMMdd}/";
            fileTypes = _config["UploadConfig:File:ExtName"].Split('|');
            fileMaxSize = Convert.ToInt64(_config["UploadConfig:File:Size"]);


        }

        #region  上传附件
        /// <summary>
        /// 上传附件
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        public async Task<ActionResult> UploadFile(IFormFile file, string fileName, string tempDirectory, int index, int total, int totalSize = 0)
        {
            if (file == null)
            {
                return BadRequest("请选择上传文件");
            }
            //var oldFileName = fileName;// file.FileName;//原文件名
            var extName = Path.GetExtension(fileName).ToLower(); //扩展名
            //var size = file.Length;

            if (!fileTypes.Contains(extName))    //"格式";
            {
                return BadRequest("文件格式错误");
            }

            if (file.Length > fileMaxSize * 1024 * 1024)    //"大小";
            {
                return BadRequest($"大小不能超过{fileMaxSize}M");
            }

            var saveName = DateTime.Now.Ticks.ToString() + extName;//保存的文件名
            string tmp = Path.Combine(fileUploadPath, tempDirectory) + "/";//临时保存分块的目录
            try
            {
                using (var stream = file.OpenReadStream())
                {
                    var strmd5 = GetMD5Value(stream);
                    //if (md5 == strmd5)//校验MD5值
                    //{
                    //}

                    if (await Save(stream, tmp, index.ToString()))
                    {
                        var uploadpath = fileUploadPath + saveName;//返回文件的相对路径
                        bool mergeOk = false;
                        if (total == index)
                        {
                            mergeOk = await FileMerge(tmp, fileUploadPath, saveName);
                            if (mergeOk)
                            {
                                Logger.Write.Info($"文件上传成功：{fileUploadPath + saveName}");
                            }
                        }

                        Dictionary<string, object> result = new Dictionary<string, object>();
                        result.Add("number", index);
                        result.Add("mergeOk", mergeOk);
                        result.Add("filename", saveName);
                        return Json(result);
                    }
                    else
                    {
                        return BadRequest("上传失败");
                    }
                }
            }
            catch (Exception ex)
            {
                Directory.Delete(_hostingEnv.WebRootPath + tmp);//删除文件夹
                Logger.Write.Error($"文件上传异常：{ex.Message}");
                return BadRequest("上传失败");
            }

        }
        /// <summary>
        /// 合并文件
        /// </summary>
        /// <param name="tmpDirectory">临时上传目录</param>        
        /// <param name="path">上传目录</param>
        /// <param name="saveFileName">保存之后新文件名</param>
        /// <returns></returns>
        private async Task<bool> FileMerge(string tmpDirectory, string path, string saveName)
        {
            try
            {
                var tmpPath = _hostingEnv.WebRootPath + tmpDirectory;//获得临时目录下面的所有文件
                var serverPath = Path.Combine(_hostingEnv.WebRootPath + path, saveName);//最终保存的文件路径
                var files = Directory.GetFiles(tmpPath);

                using (var fs = new FileStream(serverPath, FileMode.Create))
                {
                    foreach (var part in files.OrderBy(x => x.Length).ThenBy(x => x))
                    {
                        var bytes = System.IO.File.ReadAllBytes(part);
                        await fs.WriteAsync(bytes, 0, bytes.Length);
                        bytes = null;
                        System.IO.File.Delete(part);//删除分块
                    }
                    fs.Close();

                    Directory.Delete(tmpPath);//删除临时目录
                    return true;
                }
            }
            catch (Exception ex)
            {
                Logger.Write.Error($"文件合并异常：{ex.Message}");
                return false;
            }

        }

        #endregion       

        /// <summary>
        /// 文件保存到本地
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="path"></param>
        /// <param name="saveName"></param>
        /// <returns></returns>
        private async Task<bool> Save(Stream stream, string path, string saveName)
        {
            try
            {
                var serverPath = _hostingEnv.WebRootPath + path;
                if (!Directory.Exists(serverPath))
                {
                    Directory.CreateDirectory(serverPath);
                }

                await Task.Run(() =>
                {
                    FileStream fs = new FileStream(serverPath + saveName, FileMode.Create);
                    stream.Position = 0;
                    stream.CopyTo(fs);
                    fs.Close();
                });
                return true;

            }
            catch (Exception ex)
            {
                Logger.Write.Error($"文件保存异常：{ex.Message}");
                return false;
            }
        }


        /// <summary>
        /// 计算文件的MD5值
        /// </summary>
        /// <param name="obj">类型只能为string or stream，否则将会抛出错误</param>
        /// <returns>文件的MD5值</returns>
        private string GetMD5Value(object obj)
        {
            MD5 md5Hash = MD5.Create();
            byte[] data = null;
            switch (obj)
            {
                case string str:
                    data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(str));
                    break;
                case Stream stream:
                    data = md5Hash.ComputeHash(stream);
                    break;
                case null:
                    throw new ArgumentException("参数不能为空");
                default:
                    throw new ArgumentException("参数类型错误");
            }

            return BitConverter.ToString(data).Replace("-", "");
        }
    }
}